import random
from asyncio import run as async_run
from hashlib import sha3_256
from logging import basicConfig as logConfig
from logging import getLogger
from os import environ, listdir, makedirs
from os.path import getsize

from dotenv import load_dotenv
from telebot.async_telebot import AsyncTeleBot

makedirs("uploads", exist_ok=True)

load_dotenv()
GROUP_WHITELIST = environ["RANDIMGBOT_GROUP_WHITELIST"].split(",")
DATADIR = environ["RANDIMGBOT_DATADIR"].split("|")
ADMIN_TOKEN = sha3_256(random.randbytes(1488)).hexdigest()
bot = AsyncTeleBot(environ["TELEGRAM_TOKEN"], parse_mode="MARKDOWN")

logConfig(level="INFO")
botLogger = getLogger("bot")

HANDLE_CACHE: dict[str, str] = {}


def human_size(byte_size: int) -> str:
    """
    transforms byte_size into string, containing human-readable data size
    """
    if byte_size > 1073741824:
        return f"{round(byte_size / 1073741824, 2)} GB"
    if byte_size > 1048576:
        return f"{round(byte_size / 1048576, 2)} MB"
    if byte_size > 1024:
        return f"{round(byte_size / 1024, 2)} KB"
    return f"{byte_size} B"


def read_file(filename: str) -> bytes:
    with open(filename, "rb") as f:
        return f.read()


async def send_image(message, filename):
    """Sends an image"""
    botLogger.info("%d %s", message.chat.id, filename)
    fsize = getsize(filename)
    if fsize > 10000000:
        await bot.reply_to(
            message,
            f"жребий выпал на слишком большую картинку: {filename}, {human_size(fsize)}",
        )
        return

    file = HANDLE_CACHE.get(filename) or read_file(filename)

    try:
        HANDLE_CACHE[filename] = (
            (
                await bot.send_photo(
                    message.chat.id,
                    file,
                    caption=filename,
                    reply_to_message_id=message.id,
                )
            )
            .photo[-1]
            .file_id
        )
    except Exception as exc:
        botLogger.error("%d %s - %s", message.chat.id, filename, str(exc))
        await bot.reply_to(message, f"критический провал: {exc}")


@bot.message_handler(commands=["start", "help"])
async def cmd_start(message):
    botLogger.info("/start from %d", message.chat.id)
    if str(message.chat.id) in GROUP_WHITELIST:
        await bot.reply_to(message, "/random /potato /g /q")


@bot.message_handler(commands=["random", "potato", "g", "q"])
async def random_photo(message):
    if str(message.chat.id) in GROUP_WHITELIST:
        dd = random.choice(DATADIR)
        images = listdir(dd)
        filename = f"{dd}/{random.choice(images)}"
        await send_image(message, filename)


@bot.message_handler(commands=["ls"])
async def ls(message):
    if str(message.chat.id) in GROUP_WHITELIST:
        botLogger.info("%d %s", message.chat.id, message.text)
        try:
            await bot.reply_to(
                message, str(listdir(message.text[message.text.find(" ") + 1 :]))
            )
        except Exception as exc:
            botLogger.error("%d %s - %s", message.chat.id, message.text, str(exc))
            await bot.reply_to(message, f"критический провал: {exc}")


@bot.message_handler(commands=["HAKBACD", "obosralVelyacika"])
async def hakbacd(m):
    if str(m.chat.id) in GROUP_WHITELIST:
        await bot.reply_to(m, "(TODO)")


@bot.message_handler(commands=["nasratVuglu"])
async def nasratVuglu(m):
    if str(m.chat.id) in GROUP_WHITELIST:
        await bot.reply_to(m, "💩💩💩")


@bot.message_handler(commands=["ping"])
async def ping(m):
    await bot.reply_to(m, "pong")


@bot.message_handler(commands=[f"{ADMIN_TOKEN}:manual"])
async def manual(m):
    if str(m.chat.id) not in GROUP_WHITELIST:
        await bot.reply_to(m, "иди нахуй")
        return

    await send_image(m, m.text[m.text.find(" ") + 1 :])


@bot.message_handler(commands=["get"])
async def get(message):
    if str(message.chat.id) not in GROUP_WHITELIST:
        await bot.reply_to(message, "иди нахуй")
        return

    handle = message.text[message.text.find(" ") + 1 :]

    if "/" in handle or ".." in handle:
        await bot.reply_to(message, "пошёл нахуй")
        return

    await send_image(message, f"uploads/{handle}.jpg")


@bot.message_handler(content_types=["photo"])
async def upload(message):
    if not (
        message.caption
        and message.caption.startswith("/upload")
        and str(message.chat.id) in GROUP_WHITELIST
    ):
        return

    try:
        file = await bot.get_file(message.photo[-1].file_id)
        data = await bot.download_file(file.file_path)
        hashsum = sha3_256(data).hexdigest()
        HANDLE_CACHE[f"uploads/{hashsum}.jpg"] = message.photo[-1].file_id

        botLogger.info("%d %s", message.chat.id, f"uploads/{hashsum}.jpg")
        with open(f"uploads/{hashsum}.jpg", "wb") as file:
            file.write(data)
        await bot.reply_to(message, f"скачал; твой файл - {hashsum}")
    except Exception as exc:
        await bot.reply_to(message, f"критический провал: {exc}")


botLogger.info("ADMIN_TOKEN: %s", ADMIN_TOKEN)
async_run(bot.polling())
